home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / lynx-2.4 / WWW / Library / Implementation / HTFTP.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-28  |  53.6 KB  |  1,929 lines

  1. /*            File Transfer Protocol (FTP) Client
  2. **            for a WorldWideWeb browser
  3. **            ===================================
  4. **
  5. **    A cache of control connections is kept.
  6. **
  7. ** Note: Port allocation
  8. **
  9. **    It is essential that the port is allocated by the system, rather
  10. **    than chosen in rotation by us (POLL_PORTS), or the following
  11. **    problem occurs.
  12. **
  13. **    It seems that an attempt by the server to connect to a port which has
  14. **    been used recently by a listen on the same socket, or by another
  15. **    socket this or another process causes a hangup of (almost exactly)
  16. **    one minute. Therefore, we have to use a rotating port number.
  17. **    The problem remains that if the application is run twice in quick
  18. **    succession, it will hang for what remains of a minute.
  19. **
  20. ** Authors
  21. **    TBL    Tim Berners-lee <timbl@info.cern.ch>
  22. **    DD    Denis DeLaRoca 310 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
  23. **      LM      Lou Montulli <montulli@ukanaix.cc.ukans.edu>
  24. **      FM      Foteos Macrides <macrides@sci.wfeb.edu>
  25. ** History:
  26. **     2 May 91    Written TBL, as a part of the WorldWideWeb project.
  27. **    15 Jan 92    Bug fix: close() was used for NETCLOSE for control soc
  28. **    10 Feb 92    Retry if cached connection times out or breaks
  29. **     8 Dec 92    Bug fix 921208 TBL after DD
  30. **    17 Dec 92    Anon FTP password now just WWWuser@ suggested by DD
  31. **            fails on princeton.edu!
  32. **    27 Dec 93 (FM)  Fixed up so FTP now works with VMS hosts.  Path
  33. **            must be Unix-style and cannot include the device
  34. **            or top directory.
  35. **      ?? ??? ?? (LM)  Added code to prompt and send passwords for non
  36. **            anonymous FTP
  37. **      25 Mar 94 (LM)  Added code to recognize different ftp server types
  38. **                      and code to parse dates and sizes on most hosts.
  39. **    27 Mar 93 (FM)  Added code for getting dates and sizes on VMS hosts.
  40. **
  41. ** Options:
  42. **    LISTEN        We listen, the other guy connects for data.
  43. **            Otherwise, other way round, but problem finding our
  44. **            internet address!
  45. **
  46. ** Notes:
  47. **                 Portions Copyright 1994 Trustees of Dartmouth College
  48. **             Code for recognizing different FTP servers and
  49. **            parsing "ls -l" output taken from Macintosh Fetch
  50. **            program with permission from Jim Matthews,
  51. **            Dartmouth Software Development Team.
  52. */
  53.  
  54. #define LISTEN     /* @@@@ Test LJM */
  55.  
  56. /*
  57. BUGS:    @@@      Limit connection cache size!
  58.         Error reporting to user.
  59.         400 & 500 errors are acked by user with windows.
  60.         Use configuration file for user names
  61.         
  62. **        Note for portablility this version does not use select() and
  63. **        so does not watch the control and data channels at the
  64. **        same time.
  65. */        
  66.  
  67. #include "HTUtils.h"
  68. #include "tcp.h"
  69.  
  70. #include "HTAlert.h"
  71.  
  72. #include "HTFTP.h"    /* Implemented here */
  73.  
  74. /* this define should be in HTFont.h :( */
  75. #define HT_NON_BREAK_SPACE ((char)1)   /* For now */
  76.  
  77. #define REPEAT_PORT    /* Give the port number for each file */
  78. #define REPEAT_LISTEN    /* Close each listen socket and open a new one */
  79.  
  80. /* define POLL_PORTS         If allocation does not work, poll ourselves.*/
  81. #define LISTEN_BACKLOG 2    /* Number of pending connect requests (TCP)*/
  82.  
  83. #define FIRST_TCP_PORT    1024    /* Region to try for a listening port */
  84. #define LAST_TCP_PORT    5999    
  85.  
  86. #define LINE_LENGTH 256
  87. #define COMMAND_LENGTH 256
  88.  
  89. #include "HTParse.h"
  90. #include "HTTCP.h"
  91. #include "HTAnchor.h"
  92. #include "HTFile.h"    /* For HTFileFormat() */
  93. #include "HTBTree.h"
  94. #include "HTChunk.h"
  95. #include "HTAlert.h"
  96. #ifndef IPPORT_FTP
  97. #define IPPORT_FTP    21
  98. #endif
  99.  
  100. #include "LYLeaks.h"
  101.  
  102. #ifdef REMOVED_CODE
  103. extern char *malloc();
  104. extern void free();
  105. extern char *strncpy();
  106. #endif
  107.  
  108. typedef struct _connection {
  109.     struct _connection *    next;    /* Link on list     */
  110.     u_long            addr;    /* IP address        */
  111.     int                socket;    /* Socket number for communication */
  112.     BOOL            binary; /* Binary mode? */
  113. } connection;
  114.  
  115. #ifndef NIL
  116. #define NIL 0
  117. #endif
  118.  
  119. /*        Hypertext object building machinery
  120. */
  121. #include "HTML.h"
  122.  
  123. #define PUTC(c) (*targetClass.put_character)(target, c)
  124. #define PUTS(s) (*targetClass.put_string)(target, s)
  125. #define START(e) (*targetClass.start_element)(target, e, 0, 0)
  126. #define END(e) (*targetClass.end_element)(target, e)
  127. #define FREE_TARGET (*targetClass._free)(target)
  128. #define ABORT_TARGET (*targetClass._free)(target)
  129. struct _HTStructured {
  130.     CONST HTStructuredClass *    isa;
  131.     /* ... */
  132. };
  133.  
  134.  
  135. /*    Global Variables
  136. **    ---------------------
  137. */
  138. PUBLIC BOOLEAN HTfileSortMethod = FILE_BY_NAME;
  139.  
  140. /*    Module-Wide Variables
  141. **    ---------------------
  142. */
  143. PRIVATE connection * connections =0;    /* Linked list of connections */
  144. PRIVATE char    response_text[LINE_LENGTH+1];/* Last response from NewsHost */
  145. PRIVATE connection * control;        /* Current connection */
  146. PRIVATE int    data_soc = -1;        /* Socket for data transfer =invalid */
  147.  
  148. #define GENERIC_SERVER    0
  149. #define MACHTEN_SERVER     1
  150. #define UNIX_SERVER     2
  151. #define VMS_SERVER     3
  152. #define CMS_SERVER     4
  153. #define DCTS_SERVER        5
  154. #define TCPC_SERVER    6
  155. #define PETER_LEWIS_SERVER    7
  156. #define NCSA_SERVER    8
  157.  
  158. PRIVATE int     server_type = GENERIC_SERVER;   /* the type of ftp host */
  159. PRIVATE int     unsure_type = FALSE;            /* sure about the type? */
  160. PRIVATE BOOLEAN use_list = FALSE;        /* use the LIST command? */
  161.  
  162. PRIVATE interrupted_in_next_data_char = FALSE;
  163.  
  164. #ifdef POLL_PORTS
  165. PRIVATE    unsigned short    port_number = FIRST_TCP_PORT;
  166. #endif
  167.  
  168. #ifdef LISTEN
  169. PRIVATE int     master_socket = -1;    /* Listening socket = invalid    */
  170. PRIVATE char    port_command[255];    /* Command for setting the port */
  171. PRIVATE fd_set    open_sockets;         /* Mask of active channels */
  172. PRIVATE int    num_sockets;          /* Number of sockets to scan */
  173. #else
  174. PRIVATE    unsigned short    passive_port;    /* Port server specified for data */
  175. #endif
  176.  
  177.  
  178. #define NEXT_CHAR HTGetCharacter()    /* Use function in HTFormat.c */
  179.  
  180. #define DATA_BUFFER_SIZE 2048
  181. PRIVATE char data_buffer[DATA_BUFFER_SIZE];        /* Input data buffer */
  182. PRIVATE char * data_read_pointer;
  183. PRIVATE char * data_write_pointer;
  184. #define NEXT_DATA_CHAR next_data_char()
  185.  
  186.  
  187. /*    Procedure: Read a character from the data connection
  188. **    ----------------------------------------------------
  189. */
  190. PRIVATE char next_data_char
  191. NOARGS
  192. {
  193.     int status;
  194.     if (data_read_pointer >= data_write_pointer) {
  195.     status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE);
  196.       if (status == HT_INTERRUPTED)
  197.         interrupted_in_next_data_char = 1;
  198.       if (status <= 0)
  199.         return (char)-1;
  200.       data_write_pointer = data_buffer + status;
  201.       data_read_pointer = data_buffer;
  202.     }
  203. #ifdef NOT_ASCII
  204.     {
  205.         char c = *data_read_pointer++;
  206.     return FROMASCII(c);
  207.     }
  208. #else
  209.     return *data_read_pointer++;
  210. #endif
  211. }
  212.  
  213.  
  214. /*    Close an individual connection
  215. **
  216. */
  217. #ifdef __STDC__
  218. PRIVATE int close_connection(connection * con)
  219. #else
  220. PRIVATE int close_connection(con)
  221.     connection *con;
  222. #endif
  223. {
  224.     connection * scan;
  225.     int status = NETCLOSE(con->socket);
  226.     if (TRACE)
  227.         fprintf(stderr, "FTP: Closing control socket %d\n", con->socket);
  228.     if (connections==con) {
  229.         connections = con->next;
  230.     return status;
  231.     }
  232.     for(scan=connections; scan; scan=scan->next) {
  233.         if (scan->next == con) {
  234.         scan->next = con->next;    /* Unlink */
  235.         if (control==con) control = (connection*)0;
  236.         return status;
  237.     } /*if */
  238.     } /* for */
  239.     return -1;        /* very strange -- was not on list. */
  240. }
  241.  
  242. PRIVATE char *help_message_buffer = 0;  /* global :( */
  243.  
  244. PRIVATE void init_help_message_cache NOARGS
  245. {
  246.     if(help_message_buffer) free(help_message_buffer);
  247.     help_message_buffer = 0;
  248. }
  249.  
  250. PRIVATE void help_message_cache_add ARGS1(char *,string)
  251. {
  252.     if(help_message_buffer)
  253.         StrAllocCat(help_message_buffer, string);
  254.     else    
  255.         StrAllocCopy(help_message_buffer, string);
  256.  
  257.     if(TRACE)
  258.     fprintf(stderr,"Adding message to help cache: %s\n",string);
  259. }
  260.  
  261. PRIVATE char *help_message_cache_non_empty NOARGS
  262. {
  263.   return(help_message_buffer);
  264. }
  265. PRIVATE char *help_message_cache_contents NOARGS
  266. {
  267.    return(help_message_buffer);
  268. }
  269.  
  270. /*    Execute Command and get Response
  271. **    --------------------------------
  272. **
  273. **    See the state machine illustrated in RFC959, p57. This implements
  274. **    one command/reply sequence.  It also interprets lines which are to
  275. **    be continued, which are marked with a "-" immediately after the
  276. **    status code.
  277. **
  278. **    Continuation then goes on until a line with a matching reply code
  279. **    an a space after it.
  280. **
  281. ** On entry,
  282. **    con    points to the connection which is established.
  283. **    cmd    points to a command, or is NIL to just get the response.
  284. **
  285. **    The command is terminated with the CRLF pair.
  286. **
  287. ** On exit,
  288. **    returns:  The first digit of the reply type,
  289. **          or negative for communication failure.
  290. */
  291. #ifdef __STDC__
  292. PRIVATE int response(char * cmd)
  293. #else
  294. PRIVATE int response(cmd)
  295.     char * cmd;
  296. #endif
  297. {
  298.     int result;                /* Three-digit decimal code */
  299.     int    continuation_response = -1;
  300.     int status;
  301.     extern int interrupted_in_htgetcharacter;
  302.     
  303.     if (!control) {
  304.           if(TRACE)
  305.           fprintf(stderr, "FTP: No control connection set up!!\n");
  306.       return -99;
  307.     }
  308.     
  309.     if (cmd) {
  310.     
  311.     if (TRACE)
  312.         fprintf(stderr, "  Tx: %s", cmd);
  313.  
  314. #ifdef NOT_ASCII
  315.     {
  316.         char * p;
  317.         for(p=cmd; *p; p++) {
  318.             *p = TOASCII(*p);
  319.         }
  320.     }
  321. #endif 
  322.     status = NETWRITE(control->socket, cmd, (int)strlen(cmd));
  323.     if (status<0) {
  324.         if (TRACE)
  325.             fprintf(stderr, 
  326.                 "FTP: Error %d sending command: closing socket %d\n",
  327.             status, control->socket);
  328.         close_connection(control);
  329.         return status;
  330.     }
  331.     }
  332.  
  333.     do {
  334.     char *p = response_text;
  335.     for(;;) {  
  336.         if (((*p++=NEXT_CHAR) == LF)
  337.             || (p == &response_text[LINE_LENGTH])) {
  338.  
  339.         char continuation;
  340.  
  341.             if (interrupted_in_htgetcharacter)
  342.                   {
  343.                     if (TRACE)
  344.                       fprintf (stderr,
  345.                   "FTP: Interrupted in HTGetCharacter, apparently.\n");
  346.                     NETCLOSE (control->socket);
  347.                     control->socket = -1;
  348.                     return HT_INTERRUPTED;
  349.                   }
  350.  
  351.         *p=0;            /* Terminate the string */
  352.         if (TRACE)
  353.             fprintf(stderr, "    Rx: %s", response_text);
  354.  
  355.                 /* Check for login or help messages */
  356.         if(!strncmp(response_text,"230-",4) ||
  357.            !strncmp(response_text,"250-",4))
  358.             help_message_cache_add(response_text+4);
  359.  
  360.         sscanf(response_text, "%d%c", &result, &continuation);
  361.         if  (continuation_response == -1) {
  362.             if (continuation == '-')  /* start continuation */
  363.                 continuation_response = result;
  364.         } else {     /* continuing */
  365.             if (continuation_response == result
  366.                 && continuation == ' ')
  367.                 continuation_response = -1;    /* ended */
  368.         }    
  369.         break;        
  370.         } /* if end of line */
  371.         
  372.         if (interrupted_in_htgetcharacter)
  373.                {
  374.                     if (TRACE)
  375.                       fprintf (stderr,
  376.                 "FTP: Interrupted in HTGetCharacter, apparently.\n");
  377.                     NETCLOSE (control->socket);
  378.                     control->socket = -1;
  379.                     return HT_INTERRUPTED;
  380.                }
  381.  
  382.         if (*(p-1) == (char) EOF) {
  383.         if(TRACE)
  384.             fprintf(stderr, "Error on rx: closing socket %d\n",
  385.                     control->socket);
  386.         strcpy(response_text, "000 *** TCP read error on response\n");
  387.             close_connection(control);
  388.             return -1;    /* End of file on response */
  389.         }
  390.     } /* Loop over characters */
  391.  
  392.     } while (continuation_response != -1);
  393.     
  394.     if (result==421) {
  395.     if(TRACE)
  396.         fprintf(stderr, "FTP: They close so we close socket %d\n",
  397.                 control->socket);
  398.     close_connection(control);
  399.     return -1;
  400.     }
  401.     return result/100;
  402. }
  403.  
  404. /* this function should try to set the macintosh server into binary mode
  405.  */
  406. PRIVATE int set_mac_binary NOARGS
  407. {
  408.     /* try to set mac binary mode */
  409.     return(2 == response("MACB\r\n"));
  410. }
  411.  
  412. /* This function gets the current working directory to help
  413.  * determine what kind of host it is
  414.  */
  415.  
  416. PRIVATE void get_ftp_pwd ARGS2(int *,server_type, BOOLEAN *,use_list) {
  417.  
  418.     char *cp;
  419.     /* get the working directory (to see what it looks like) */
  420.     int status = response("PWD\r\n");
  421.     if (status < 0)
  422.         return;
  423.     else 
  424.      {
  425.  
  426.     cp = strchr(response_text+5,'"');
  427.     if(cp) *cp = '\0';
  428.         if (*server_type == TCPC_SERVER)
  429.          {
  430.             *server_type = response_text[5] == '/' ? NCSA_SERVER : TCPC_SERVER;
  431.          }
  432.         else if (response_text[5] == '/')
  433.          {
  434.             /* path names beginning with / imply Unix,
  435.          * right? 
  436.          */
  437.          if(set_mac_binary())
  438.            *server_type = NCSA_SERVER;
  439.          else
  440.         {
  441.                    *server_type = UNIX_SERVER;
  442.                    *use_list = TRUE;
  443.         }
  444.          return;
  445.          }
  446.         else if (response_text[strlen(response_text)-1] == ']')
  447.          {
  448.              /* path names ending with ] imply VMS, right? */
  449.              *server_type = VMS_SERVER;
  450.          *use_list = TRUE;
  451.          }
  452.         else
  453.              *server_type = GENERIC_SERVER;
  454.  
  455.         if ((*server_type == NCSA_SERVER) ||
  456.                (*server_type == TCPC_SERVER) ||
  457.                     (*server_type == PETER_LEWIS_SERVER))
  458.             set_mac_binary();
  459.      }
  460. }
  461.  
  462. /*    Get a valid connection to the host
  463. **    ----------------------------------
  464. **
  465. ** On entry,
  466. **    arg    points to the name of the host in a hypertext address
  467. ** On exit,
  468. **    returns    <0 if error
  469. **        socket number if success
  470. **
  471. **    This routine takes care of managing timed-out connections, and
  472. **    limiting the number of connections in use at any one time.
  473. **
  474. **    It ensures that all connections are logged in if they exist.
  475. **    It ensures they have the port number transferred.
  476. */
  477. PRIVATE int get_connection ARGS1 (CONST char *,arg)
  478. {
  479.     int status;
  480.     char * command;
  481.     connection * con = (connection *)malloc(sizeof(connection));
  482.  
  483.     char * username=0;
  484.     char * password=0;
  485.     static char *user_entered_password=0;
  486.     static char *last_username_and_host=0;
  487.  
  488.     /* init control struct
  489.      */
  490.     memset((void *)con, 0, sizeof(connection));
  491.     
  492.     if (!arg) return -1;        /* Bad if no name sepcified    */
  493.     if (!*arg) return -1;        /* Bad if name had zero length    */
  494.  
  495. /* Get node name:
  496. */
  497.     {
  498.     char *p1 = HTParse(arg, "", PARSE_HOST);
  499.     char *p2 = strrchr(p1, '@');    /* user? */
  500.     char * pw=0;
  501.  
  502.     if (p2!=NULL) {
  503.         username = p1;
  504.         *p2=0;            /* terminate */
  505.         p1 = p2+1;            /* point to host */
  506.         pw = strchr(username, ':');
  507.         if (pw) {
  508.             *pw++ = 0;
  509.         password = pw;
  510.         }
  511.  
  512.         /* if the password doesn't exist then we are going to have
  513.          * to ask the user for it.  The only problem is that we
  514.          * don't want to ask for it every time, so we will store
  515.          * away in a primitive fashion.
  516.          */
  517.         if(!password) {
  518.         char tmp[256];
  519.  
  520.         sprintf(tmp,"%s@%s",username,p1);
  521.         /* if the user@host is not equal to the last time through
  522.          * or user_entered_password has no data then we need
  523.          * to ask the user for the password
  524.          */
  525.         if(!last_username_and_host ||
  526.             strcmp(tmp,last_username_and_host) ||
  527.                         !user_entered_password) {
  528.  
  529.             StrAllocCopy(last_username_and_host,tmp);
  530.             sprintf(tmp,"Enter password for user %s@%s:",username,p1);
  531.             if(user_entered_password) 
  532.             free(user_entered_password);
  533.             user_entered_password = (char *)HTPromptPassword(tmp);
  534.  
  535.         } /* else we already know the password */
  536.         password = user_entered_password;
  537.         }
  538.     }
  539.  
  540.         if (!username) free(p1);
  541.     } /* scope of p1 */
  542.  
  543.         
  544.   con->socket = -1;
  545.   status = HTDoConnect (arg, "FTP", IPPORT_FTP, &con->socket);
  546.    
  547.   if (status < 0)
  548.     {
  549.       if (TRACE)
  550.         {
  551.           if (status == HT_INTERRUPTED)
  552.             fprintf (stderr,
  553.                      "FTP: Interrupted on connect\n");
  554.           else
  555.             fprintf(stderr,
  556.                     "FTP: Unable to connect to remote host for `%s'.\n",
  557.                     arg);
  558.         }
  559.       if (status == HT_INTERRUPTED)
  560.         _HTProgress ("Connection interrupted.");
  561.       if (con->socket != -1)
  562.         {
  563.           NETCLOSE(con->socket);
  564.         }
  565.     
  566.       if (username)
  567.           free(username);
  568.       free(con);
  569.       return status;                    /* Bad return */
  570.     }
  571.  
  572.     
  573.     if (TRACE) 
  574.      fprintf(stderr, "FTP connected, socket %d\n", con);
  575.     control = con;        /* Current control connection */
  576.  
  577.     /* Initialise buffering for contron connection */
  578.     HTInitInput(control->socket);
  579.  
  580.  
  581. /*    Now we log in        Look up username, prompt for pw.
  582. */
  583.   {
  584.     int status = response((char *)0);    /* Get greeting */
  585.  
  586.     if (status == HT_INTERRUPTED)
  587.       {
  588.         if (TRACE)
  589.           fprintf (stderr,
  590.                    "FTP: Interrupted at beginning of login.\n");
  591.         _HTProgress ("Connection interrupted.");
  592.         NETCLOSE(control->socket);
  593.         control->socket = -1;
  594.         return HT_INTERRUPTED;
  595.       }
  596.     if (status == 2) {        /* Send username */
  597.     if (username) {
  598.         command = (char*)malloc(10+strlen(username)+2+1);
  599.         if (command == NULL) outofmem(__FILE__, "get_connection");
  600.         sprintf(command, "USER %s%c%c", username, CR, LF);
  601.     } else {
  602.         command = (char*)malloc(24);
  603.         if (command == NULL) outofmem(__FILE__, "get_connection");
  604.         sprintf(command, "USER anonymous%c%c", CR, LF);
  605.         }
  606.     status = response(command);
  607.     free(command);
  608.         if (status == HT_INTERRUPTED)
  609.           {
  610.             if (TRACE)
  611.               fprintf (stderr,
  612.                        "FTP: Interrupted while sending username.\n");
  613.             _HTProgress ("Connection interrupted.");
  614.             NETCLOSE(control->socket);
  615.             control->socket = -1;
  616.             return HT_INTERRUPTED;
  617.           }
  618.     }
  619.     if (status == 3) {        /* Send password */
  620.     if (password) {
  621.         command = (char*)malloc(10+strlen(password)+2+1);
  622.         if (command == NULL) outofmem(__FILE__, "get_connection");
  623.         sprintf(command, "PASS %s%c%c", password, CR, LF);
  624.     } else {
  625.         char * user = getenv("USER");
  626.         CONST char *host = HTHostName();
  627.         if (!user) user = "WWWuser";
  628.         /* If not fully qualified, suppress it as ftp.uu.net
  629.            prefers a blank to a bad name */
  630.         if (!strchr(host, '.')) host = "";
  631.  
  632.         command = (char*)malloc(20+strlen(host)+2+1);
  633.         if (command == NULL) outofmem(__FILE__, "get_connection");
  634.         sprintf(command, "PASS %s@%s%c%c", user ? user : "WWWuser",
  635.                                 host, CR, LF); /*@@*/
  636.         }
  637.     status = response(command);
  638.     free(command);
  639.         if (status == HT_INTERRUPTED)
  640.           {
  641.             if (TRACE)
  642.               fprintf (stderr,
  643.                        "FTP: Interrupted while sending password.\n");
  644.             _HTProgress ("Connection interrupted.");
  645.             NETCLOSE(control->socket);
  646.             control->socket = -1;
  647.             return HT_INTERRUPTED;
  648.           }
  649.     }
  650.     if (username) free(username);
  651.  
  652.     if (status == 3) {
  653.         char temp[80];
  654.     sprintf(temp, "ACCT noaccount%c%c", CR, LF);
  655.     status = response(temp);
  656.     if (status == HT_INTERRUPTED)
  657.           {
  658.             if (TRACE)
  659.               fprintf (stderr,
  660.                        "FTP: Interrupted while sending password.\n");
  661.             _HTProgress ("Connection interrupted.");
  662.             NETCLOSE(control->socket);
  663.             control->socket = -1;
  664.             return HT_INTERRUPTED;
  665.           }
  666.  
  667.     }
  668.     if (status !=2) {
  669.         if (TRACE)
  670.         fprintf(stderr, "FTP: Login fail: %s", response_text);
  671.         /* if (control->socket > 0) close_connection(control->socket); */
  672.         return -1;        /* Bad return */
  673.     }
  674.     if (TRACE) fprintf(stderr, "FTP: Logged in.\n");
  675.  
  676.     /** Check for host type **/
  677.     server_type = GENERIC_SERVER;    /* reset */
  678.     use_list = FALSE;             /* reset */
  679.     if ((status=response("SYST\r\n")) == 2) {
  680.                 /* we got a line -- what kind of server are we talking to? */
  681.          if (strncmp(response_text+4, "UNIX Type: L8 MAC-OS MachTen", 28) == 0)
  682.           {
  683.              server_type = MACHTEN_SERVER;
  684.          use_list = TRUE;
  685.           }
  686.          else if (strstr(response_text+4, "UNIX") != NULL)
  687.           {
  688.              server_type = UNIX_SERVER;
  689.          use_list = TRUE;
  690.           }
  691.          else if (strncmp(response_text+4, "VMS", 3) == 0)
  692.       {
  693.              server_type = VMS_SERVER;
  694.          use_list = TRUE;
  695.       }
  696.          else if ((strncmp(response_text+4, "VM/CMS", 6) == 0)
  697.                  || (strncmp(response_text+4, "VM ", 3) == 0))
  698.              server_type = CMS_SERVER;
  699.          else if (strncmp(response_text+4, "DCTS", 4) == 0)
  700.              server_type = DCTS_SERVER;
  701.          else if (strstr(response_text+4, "MAC-OS TCP/Connect II") != NULL)
  702.           {
  703.              server_type = TCPC_SERVER;
  704.              get_ftp_pwd(&server_type, &use_list);
  705.          unsure_type = TRUE;   
  706.           }
  707.          else if (strncmp(response_text+4, "MACOS Peter's Server", 20) == 0)
  708.           {
  709.              server_type = PETER_LEWIS_SERVER;
  710.              use_list = TRUE;
  711.              set_mac_binary();
  712.           }
  713.      else 
  714.       {
  715.          server_type = GENERIC_SERVER;
  716.              get_ftp_pwd(&server_type, &use_list);
  717.          unsure_type = TRUE;   
  718.       }
  719.     } else {
  720.     /* SYST fails :(  try to get the type from the PWD command */
  721.          get_ftp_pwd(&server_type, &use_list);
  722.     }
  723.  
  724. /*    Now we inform the server of the port number we will listen on
  725. */
  726. #ifdef NOTREPEAT_PORT
  727.     {
  728.         int status = response(port_command);
  729.         if (status !=2) {
  730.             if (control->socket) close_connection(control->socket);
  731.             return -status;        /* Bad return */
  732.         }
  733.         if (TRACE)
  734.             fprintf(stderr, "FTP: Port defined.\n");
  735.     }
  736. #endif
  737.     return con->socket;            /* Good return */
  738.   } /* Scope of con */
  739. }
  740.  
  741.  
  742. #ifdef LISTEN
  743.  
  744. /*    Close Master (listening) socket
  745. **    -------------------------------
  746. **
  747. **
  748. */
  749. #ifdef __STDC__
  750. PRIVATE int close_master_socket(void)
  751. #else
  752. PRIVATE int close_master_socket()
  753. #endif
  754. {
  755.     int status;
  756.     FD_CLR(master_socket, &open_sockets);
  757.     status = NETCLOSE(master_socket);
  758.     if (TRACE)
  759.         fprintf(stderr, "FTP: Closed master socket %d\n", master_socket);
  760.     master_socket = -1;
  761.     if (status<0) return HTInetStatus("close master socket");
  762.     else return status;
  763. }
  764.  
  765.  
  766. /*    Open a master socket for listening on
  767. **    -------------------------------------
  768. **
  769. **    When data is transferred, we open a port, and wait for the server to
  770. **    connect with the data.
  771. **
  772. ** On entry,
  773. **    master_socket    Must be negative if not set up already.
  774. ** On exit,
  775. **    Returns        socket number if good
  776. **            less than zero if error.
  777. **    master_socket    is socket number if good, else negative.
  778. **    port_number    is valid if good.
  779. */
  780. #ifdef __STDC__
  781. PRIVATE int get_listen_socket(void)
  782. #else
  783. PRIVATE int get_listen_socket()
  784. #endif
  785. {
  786.     struct sockaddr_in soc_address;    /* Binary network address */
  787.     struct sockaddr_in* sin = &soc_address;
  788.     int new_socket;            /* Will be master_socket */
  789.     
  790.     
  791.     FD_ZERO(&open_sockets);    /* Clear our record of open sockets */
  792.     num_sockets = 0;
  793.     
  794. #ifndef REPEAT_LISTEN
  795.     if (master_socket>=0) return master_socket;  /* Done already */
  796. #endif
  797.  
  798. /*  Create internet socket
  799. */
  800.     new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  801.     
  802.     if (new_socket<0)
  803.     return HTInetStatus("socket for master socket");
  804.     
  805.     if (TRACE)
  806.         fprintf(stderr, "FTP: Opened master socket number %d\n", new_socket);
  807.     
  808. /*  Search for a free port.
  809. */
  810.     sin->sin_family = AF_INET;        /* Family = internet, host order  */
  811.     sin->sin_addr.s_addr = INADDR_ANY; /* Any peer address */
  812. #ifdef POLL_PORTS
  813.     {
  814.         unsigned short old_port_number = port_number;
  815.     for(port_number=old_port_number+1;;port_number++){ 
  816.         int status;
  817.         if (port_number > LAST_TCP_PORT)
  818.         port_number = FIRST_TCP_PORT;
  819.         if (port_number == old_port_number) {
  820.         return HTInetStatus("bind");
  821.         }
  822.         soc_address.sin_port = htons(port_number);
  823. #ifdef SOCKS
  824.         if ((status=Rbind(new_socket,
  825. #else
  826.         if ((status=bind(new_socket,
  827. #endif /* SOCKS */
  828.             (struct sockaddr*)&soc_address,
  829.                 /* Cast to generic sockaddr */
  830.             sizeof(soc_address))) == 0)
  831.         break;
  832.         if (TRACE)
  833.             fprintf(stderr, 
  834.                 "TCP bind attempt to port %d yields %d, errno=%d\n",
  835.         port_number, status, SOCKET_ERRNO);
  836.     } /* for */
  837.     }
  838. #else
  839.     {
  840.         int status;
  841.     int address_length = sizeof(soc_address);
  842. #ifdef SOCKS
  843.     status = Rgetsockname(control->socket,
  844. #else
  845.     status = getsockname(control->socket,
  846. #endif /* SOCKS */
  847.             (struct sockaddr *)&soc_address,
  848.              &address_length);
  849.     if (status<0) return HTInetStatus("getsockname");
  850.     CTRACE(tfp, "FTP: This host is %s\n",
  851.         HTInetString(sin));
  852.     
  853.     soc_address.sin_port = 0;    /* Unspecified: please allocate */
  854. #ifdef SOCKS
  855.     status=Rbind(new_socket,
  856. #else
  857.     status=bind(new_socket,
  858. #endif /* SOCKS */
  859.         (struct sockaddr*)&soc_address,
  860.             /* Cast to generic sockaddr */
  861.         sizeof(soc_address));
  862.     if (status<0) return HTInetStatus("bind");
  863.     
  864.     address_length = sizeof(soc_address);
  865. #ifdef SOCKS
  866.     status = Rgetsockname(new_socket,
  867. #else
  868.     status = getsockname(new_socket,
  869. #endif /* SOCKS */
  870.             (struct sockaddr*)&soc_address,
  871.             &address_length);
  872.     if (status<0) return HTInetStatus("getsockname");
  873.     }
  874. #endif    
  875.  
  876.     CTRACE(tfp, "FTP: bound to port %d on %s\n",
  877.             (int)ntohs(sin->sin_port),
  878.         HTInetString(sin));
  879.  
  880. #ifdef REPEAT_LISTEN
  881.     if (master_socket>=0)
  882.         (void) close_master_socket();
  883. #endif    
  884.     
  885.     master_socket = new_socket;
  886.     
  887. /*    Now we must find out who we are to tell the other guy
  888. */
  889.     (void)HTHostName();     /* Make address valid - doesn't work*/
  890.     sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d%c%c",
  891.             (int)*((unsigned char *)(&sin->sin_addr)+0),
  892.             (int)*((unsigned char *)(&sin->sin_addr)+1),
  893.             (int)*((unsigned char *)(&sin->sin_addr)+2),
  894.             (int)*((unsigned char *)(&sin->sin_addr)+3),
  895.             (int)*((unsigned char *)(&sin->sin_port)+0),
  896.             (int)*((unsigned char *)(&sin->sin_port)+1),
  897.             CR, LF);
  898.  
  899.  
  900. /*    Inform TCP that we will accept connections
  901. */
  902. #ifdef SOCKS
  903.     if (Rlisten(master_socket, 1)<0) {
  904. #else
  905.     if (listen(master_socket, 1)<0) {
  906. #endif /* SOCKS */
  907.     master_socket = -1;
  908.     return HTInetStatus("listen");
  909.     }
  910.     CTRACE(tfp, "TCP: Master socket(), bind() and listen() all OK\n");
  911.     FD_SET(master_socket, &open_sockets);
  912.     if ((master_socket+1) > num_sockets) num_sockets=master_socket+1;
  913.  
  914.     return master_socket;        /* Good */
  915.  
  916. } /* get_listen_socket */
  917. #endif
  918.  
  919. typedef struct _EntryInfo {
  920.     char *       filename;
  921.     char *       type;
  922.     char *       date;
  923.     unsigned int size;
  924.     BOOLEAN      display;  /* show this entry? */
  925. } EntryInfo;
  926.  
  927. PRIVATE void free_entryinfo_struct_contents ARGS1(EntryInfo *,entry_info)
  928. {
  929.     if(entry_info) {
  930.         if(entry_info->filename) free(entry_info->filename);
  931.         if(entry_info->type) free(entry_info->type);
  932.         if(entry_info->date) free(entry_info->date);
  933.     }
  934.    /* dont free the struct */
  935. }
  936.  
  937. /*
  938.  * is_ls_date() --
  939.  *      Return TRUE if s points to a string of the form:
  940.  *              "Sep  1  1990 " or
  941.  *              "Sep 11 11:59 " or
  942.  *              "Dec 12 1989  " or
  943.  *              "FCv 23 1990  " ...
  944.  */
  945. PRIVATE BOOLEAN is_ls_date ARGS1(char *,s)
  946. {
  947.         /* must start with three alpha characters */
  948.         if (!isalpha(*s++) || !isalpha(*s++) || !isalpha(*s++))
  949.                 return FALSE;
  950.  
  951.         /* space */
  952.         if (*s++ != ' ')
  953.                 return FALSE;
  954.  
  955.         /* space or digit */
  956.         if ((*s != ' ') && !isdigit(*s))
  957.                 return FALSE;
  958.         s++;
  959.  
  960.         /* digit */
  961.         if (!isdigit(*s++))
  962.                 return FALSE;
  963.  
  964.         /* space */
  965.         if (*s++ != ' ')
  966.                 return FALSE;
  967.  
  968.         /* space or digit */
  969.         if ((*s != ' ') && !isdigit(*s))
  970.                 return FALSE;
  971.         s++;
  972.  
  973.         /* digit */
  974.         if (!isdigit(*s++))
  975.                 return FALSE;
  976.  
  977.         /* colon or digit */
  978.         if ((*s != ':') && !isdigit(*s))
  979.                 return FALSE;
  980.         s++;
  981.  
  982.         /* digit */
  983.         if (!isdigit(*s++))
  984.                 return FALSE;
  985.  
  986.         /* space or digit */
  987.         if ((*s != ' ') && !isdigit(*s))
  988.                 return FALSE;
  989.         s++;
  990.  
  991.         /* space */
  992.         if (*s++ != ' ')
  993.                 return FALSE;
  994.  
  995.         return TRUE;
  996. } /* is_ls_date() */
  997.  
  998.  
  999. /*
  1000.  * parse_ls_line() --
  1001.  *      Extract the name, size, and date from an ls -l line.
  1002.  */
  1003. PRIVATE void parse_ls_line ARGS2(char *,line, EntryInfo *,entry_info)
  1004. {
  1005.         short   i, j;
  1006.         int    base=1;
  1007.     int    size_num=0;
  1008.  
  1009.         for (i = strlen(line) - 1;
  1010.             (i > 13) && (!isspace(line[i]) || !is_ls_date(&line[i-12])); i--)
  1011.                 ; /* null body */
  1012.         line[i] = '\0';
  1013.         if (i > 13) {
  1014.             StrAllocCopy(entry_info->date, &line[i-12]);
  1015.         /* replace the 4th location with nbsp if it is a space */
  1016.         if(entry_info->date[4] == ' ')
  1017.         entry_info->date[4] = HT_NON_BREAK_SPACE;
  1018.     } 
  1019.         j = i - 14;
  1020.         while (isdigit(line[j]))
  1021.         {
  1022.                 size_num += (line[j] - '0') * base;
  1023.                 base *= 10;
  1024.                 j--;
  1025.         }
  1026.     entry_info->size = size_num;
  1027.         StrAllocCopy(entry_info->filename, &line[i + 1]);
  1028. } /* parse_ls_line() */
  1029.  
  1030. /*
  1031.  * parse_vms_dir_entry()
  1032.  *      Format the name, date, and size from a VMS LIST line
  1033.  *      into the EntryInfo structure
  1034.  */
  1035. PRIVATE void parse_vms_dir_entry ARGS2(char *,line, EntryInfo *,entry_info)
  1036. {
  1037.         int i, j, ialloc;
  1038.         char *cp, *cpd, *cps, date[16], *sp = " ";
  1039.     time_t NowTime;
  1040.     static char ThisYear[8];
  1041.     static BOOLEAN HaveYear = FALSE; 
  1042.  
  1043.         /**  Get rid of blank lines, and information lines.  **/
  1044.         /**  Valid lines have the ';' version number token.  **/
  1045.         if (!strlen(line) || (cp=strchr(line, ';')) == NULL) {
  1046.             entry_info->display = FALSE;
  1047.             return;
  1048.         }
  1049.  
  1050.         /** Cut out file or directory name at VMS version number. **/
  1051.     *cp++ ='\0';
  1052.     StrAllocCopy(entry_info->filename,line);
  1053.  
  1054.         /** Cast VMS file and directory names to lowercase. **/
  1055.     for (i=0; entry_info->filename[i]; i++)
  1056.             entry_info->filename[i] = TOLOWER(entry_info->filename[i]);
  1057.  
  1058.         /** Uppercase terminal .z's or _z's. **/
  1059.     if ((--i > 2) && entry_info->filename[i] == 'z' &&
  1060.              (entry_info->filename[i-1] == '.' ||
  1061.             entry_info->filename[i-1] == '_'))
  1062.             entry_info->filename[i] = 'Z';
  1063.  
  1064.         /** Convert any tabs in rest of line to spaces. **/
  1065.     cps = cp-1;
  1066.         while ((cps=strchr(cps+1, '\t')) != NULL)
  1067.             *cps = ' ';
  1068.  
  1069.         /** Collapse serial spaces. **/
  1070.         i = 0; j = 1;
  1071.     cps = cp;
  1072.         while (cps[j] != '\0') {
  1073.             if (cps[i] == ' ' && cps[j] == ' ')
  1074.                 j++;
  1075.             else
  1076.                 cps[++i] = cps[j++];
  1077.         }
  1078.         cps[++i] = '\0';
  1079.  
  1080.         /** Save the current year, if we don't have it yet.  It  **/
  1081.     /** could be wrong on New Year's Eve, if some poor soul  **/
  1082.     /** is using Lynx instead of kissing his/her sweetheart. **/
  1083.     if (!HaveYear) {
  1084.         NowTime = time(NULL);
  1085.          strcpy(ThisYear, (char *)ctime(&NowTime)+20);
  1086.         ThisYear[4] = '\0';
  1087.         HaveYear = TRUE;
  1088.     }
  1089.  
  1090.         /** Track down the date. **/
  1091.         if ((cpd=strchr(cp, '-')) != NULL &&
  1092.             strlen(cpd) > 9 && isdigit(*(cpd-1)) &&
  1093.             isalpha(*(cpd+1)) && *(cpd+4) == '-') {
  1094.  
  1095.         /** Month **/
  1096.             *(cpd+4) = '\0';
  1097.             *(cpd+2) = TOLOWER(*(cpd+2));
  1098.             *(cpd+3) = TOLOWER(*(cpd+3));
  1099.         sprintf(date, "%s ", cpd+1);
  1100.         *(cpd+4) = '-';
  1101.  
  1102.         /** Day **/
  1103.         *cpd = '\0';
  1104.         if (isdigit(*(cpd-2)))
  1105.             sprintf(date+4, "%s ", cpd-2);
  1106.         else
  1107.             sprintf(date+4, " %s ", cpd-1);
  1108.         *cpd = '-';
  1109.  
  1110.         /** Time or Year **/
  1111.         if (!strncmp(ThisYear, cpd+5, 4) &&
  1112.             strlen(cpd) > 15 && *(cpd+12) == ':') {
  1113.             *(cpd+15) = '\0';
  1114.             sprintf(date+7, "%s", cpd+10);
  1115.             *(cpd+15) = ' ';
  1116.         } else {
  1117.             *(cpd+9) = '\0';
  1118.             sprintf(date+7, " %s", cpd+5);
  1119.             *(cpd+9) = ' ';
  1120.         }
  1121.  
  1122.             StrAllocCopy(entry_info->date, date);
  1123.         }
  1124.  
  1125.         /** Track down the size **/
  1126.         if ((cpd=strchr(cp, '/')) != NULL) {
  1127.             /* Appears be in used/allocated format */
  1128.             cps = cpd;
  1129.             while (isdigit(*(cps-1)))
  1130.                 cps--;
  1131.             if (cps < cpd)
  1132.                 *cpd = '\0';
  1133.             entry_info->size = atoi(cps);
  1134.             cps = cpd+1;
  1135.             while (isdigit(*cps))
  1136.                 cps++;
  1137.             *cps = '\0';
  1138.             ialloc = atoi(cpd+1);
  1139.             /* Check if used is in blocks or bytes */
  1140.             if (entry_info->size <= ialloc)
  1141.                 entry_info->size *= 512;
  1142.         }
  1143.         else if ((cps=strtok(cp, sp)) != NULL) {
  1144.             /* We just initialized on the version number */
  1145.             /* Now let's hunt for a lone, size number    */
  1146.             while ((cps=strtok(NULL, sp)) != NULL) {
  1147.                cpd = cps;
  1148.                while (isdigit(*cpd))
  1149.                    cpd++;
  1150.                if (*cpd == '\0') {
  1151.                    /* Assume it's blocks */
  1152.                    entry_info->size = atoi(cps) * 512;
  1153.                    break;
  1154.                }
  1155.            }
  1156.         }
  1157.  
  1158.         /** Wrap it up **/
  1159.         if (TRACE)
  1160.         fprintf(stderr,
  1161.                     "HTFTP: VMS filename: %s  date: %s  size: %d\n",
  1162.                     entry_info->filename,
  1163.                     entry_info->date ? entry_info->date : "",
  1164.                     entry_info->size);
  1165.         return;
  1166. } /* parse_vms_dir_entry() */
  1167.  
  1168. /*
  1169.  *     parse_dir_entry() 
  1170.  *      Given a line of LIST/NLST output in entry, return results 
  1171.  *      and a file/dir name in entry_info struct
  1172.  *
  1173.  *      If first is true, this is the first name in a directory.
  1174.  */
  1175.  
  1176. PRIVATE EntryInfo * parse_dir_entry ARGS2(char *, entry, BOOLEAN *,first)
  1177. {
  1178.         EntryInfo *entry_info;
  1179.         int  i;
  1180.         int  len;
  1181.     char *cp;
  1182.     BOOLEAN remove_size=FALSE;
  1183.  
  1184.         entry_info = (EntryInfo *)malloc(sizeof(EntryInfo));    
  1185.     entry_info->type = 0;
  1186.     entry_info->size = 0;
  1187.     entry_info->date = 0;
  1188.     entry_info->filename = 0;
  1189.     entry_info->display = TRUE;
  1190.  
  1191.         switch (server_type)
  1192.         {
  1193.         case UNIX_SERVER:
  1194.         case PETER_LEWIS_SERVER:
  1195.         case MACHTEN_SERVER:
  1196.  
  1197.                 /* interpret and edit LIST output from Unix server */
  1198.                len = strlen(entry);
  1199.         
  1200.  
  1201.            if (*first) {
  1202.  
  1203.            *first=FALSE;
  1204.                    if(!strncmp(entry, "total ", 6) ||
  1205.                                (strstr(entry, "not available") != NULL))
  1206.              {
  1207.                 entry_info->display=FALSE;
  1208.                 return(entry_info);
  1209.              }
  1210.             else if(unsure_type)
  1211.               {
  1212.                          /* this isn't really a unix server! */
  1213.                          server_type = GENERIC_SERVER;
  1214.                  entry_info->display=FALSE;
  1215.                  return(entry_info);
  1216.               }
  1217.            }
  1218.  
  1219.                /* check first character of ls -l output */
  1220.                if (TOUPPER(entry[0]) == 'D') 
  1221.          {
  1222.                    /* it's a directory */
  1223.                    StrAllocCopy(entry_info->type, "Directory"); 
  1224.            remove_size=TRUE; /* size is not useful */
  1225.          }
  1226.                else if (entry[0] == 'l')
  1227.          {
  1228.                     /* it's a symbolic link, does the user care about
  1229.              * knowing if it is symbolic?  I think so since
  1230.              * it might be a directory
  1231.              */
  1232.                     StrAllocCopy(entry_info->type, "Symbolic Link"); 
  1233.             remove_size=TRUE; /* size is not useful */
  1234.  
  1235.                     /* strip off " -> pathname" */
  1236.                     for (i = len - 1; (i > 3) && (!isspace(entry[i])
  1237.                     || (entry[i-1] != '>') 
  1238.                     || (entry[i-2] != '-')
  1239.                     || (entry[i-3] != ' ')); i--)
  1240.                              ; /* null body */
  1241.                     if (i > 3)
  1242.                       {
  1243.                         entry[i-3] = '\0';
  1244.                         len = i - 3;
  1245.                       }
  1246.                   } /* link */
  1247.  
  1248.             parse_ls_line(entry, entry_info); 
  1249.  
  1250.         if(!strcmp(entry_info->filename,"..") || 
  1251.                     !strcmp(entry_info->filename,"."))
  1252.             entry_info->display=FALSE;
  1253.         
  1254.         /* goto the bottom and get real type */
  1255.                 break;
  1256.  
  1257.         case VMS_SERVER:
  1258.                 /* Interpret and edit LIST output from VMS server */
  1259.         /* and convert information lines to zero length.  */
  1260.         parse_vms_dir_entry(entry, entry_info);
  1261.  
  1262.                 /* Get rid of any junk lines */
  1263.         if(!entry_info->display)
  1264.             return(entry_info);
  1265.  
  1266.         /** Trim off VMS directory extensions **/
  1267.         len = strlen(entry_info->filename);
  1268.                 if ((len > 4) && !strcmp(&entry_info->filename[len-4], ".dir"))
  1269.           {
  1270.             entry_info->filename[len-4] = '\0';
  1271.                     StrAllocCopy(entry_info->type, "Directory"); 
  1272.             remove_size=TRUE; /* size is not useful */
  1273.           }
  1274.         /* goto the bottom and get real type */
  1275.                 break;
  1276.  
  1277.         case CMS_SERVER:
  1278.         /* can't be directory... */
  1279.         /*
  1280.          * "entry" already equals the correct filename
  1281.          */
  1282.         StrAllocCopy(entry_info->filename,entry);
  1283.         /* goto the bottom and get real type */
  1284.                 break;
  1285.  
  1286.         case NCSA_SERVER:
  1287.         case TCPC_SERVER:
  1288.                 /* directories identified by trailing "/" characters */
  1289.         StrAllocCopy(entry_info->filename,entry);
  1290.                 len = strlen(entry);
  1291.                 if (entry[len-1] == '/')
  1292.                 {
  1293.                         /* it's a dir, remove / and mark it as such */
  1294.                         entry[len-1] = '\0';
  1295.                         StrAllocCopy(entry_info->type, "Directory");
  1296.             remove_size=TRUE; /* size is not useful */
  1297.                 }
  1298.         /* goto the bottom and get real type */
  1299.                 break;
  1300.     
  1301.     default:
  1302.         /* we cant tell if it is a directory since we only
  1303.          * did an NLST :(  List bad file types anyways? NOT!
  1304.          */
  1305.         StrAllocCopy(entry_info->filename,entry);
  1306.         return(entry_info); /* mostly empty info */
  1307.         break; /* not needed */
  1308.  
  1309.         } /* switch (server_type) */
  1310.  
  1311.  
  1312.     if(remove_size && entry_info->size)
  1313.       {
  1314.         entry_info->size = 0;
  1315.       }
  1316.  
  1317.     /* get real types eventually */
  1318.     if(!entry_info->type) {
  1319.         int i=0;
  1320.         char *cp;
  1321.             HTFormat format;
  1322.             HTAtom * encoding;  /* @@ not used at all */
  1323.             format = HTFileFormat(entry_info->filename, &encoding);
  1324.  
  1325.         if(!strncmp(HTAtom_name(format), "application",11)) 
  1326.           {
  1327.            cp = HTAtom_name(format) + 12;
  1328.            if(!strncmp(cp,"x-",2))
  1329.             cp+=2;
  1330.           }
  1331.         else
  1332.         cp = HTAtom_name(format);
  1333.  
  1334.             StrAllocCopy(entry_info->type, cp);
  1335.     }
  1336.  
  1337.     return(entry_info);
  1338.  
  1339. } /* parse_dir_entry */
  1340.  
  1341. PUBLIC int compare_EntryInfo_structs ARGS2(EntryInfo *,entry1, 
  1342.                             EntryInfo *,entry2)
  1343. {
  1344.     int status;
  1345.  
  1346.     switch(HTfileSortMethod)
  1347.       {
  1348.         case FILE_BY_SIZE:
  1349.             /* both equal or both 0 */
  1350.                         if(entry1->size == entry2->size)
  1351.                 return(strcasecomp(entry1->filename, 
  1352.                             entry2->filename));
  1353.             else
  1354.                 if(entry1->size > entry2->size)
  1355.                 return(1);
  1356.                 else
  1357.                 return(-1);
  1358.                         break;
  1359.         case FILE_BY_TYPE:
  1360.                         if(entry1->type && entry2->type) {
  1361.                             status = strcasecomp(entry1->type, entry2->type);
  1362.                 if(status)
  1363.                 return(status);
  1364.                 /* else fall to filename comparison */
  1365.             }
  1366.                         return (strcasecomp(entry1->filename, 
  1367.                             entry2->filename));
  1368.                         break;
  1369.         case FILE_BY_DATE:
  1370.                         if(entry1->date && entry2->date) {
  1371.                                 /* We really should change the type :( */
  1372.                             status = strcasecomp(entry1->date, entry2->date);
  1373.                 if(status)
  1374.                 return(status);
  1375.                 /* else fall to filename comparison */
  1376.             }
  1377.                         return (strcasecomp(entry1->filename, 
  1378.                             entry2->filename));
  1379.                         break;
  1380.         case FILE_BY_NAME:
  1381.         default:
  1382.                         return (strcasecomp(entry1->filename, 
  1383.                             entry2->filename));
  1384.       }
  1385. }
  1386.  
  1387.  
  1388. /*    Read a directory into an hypertext object from the data socket
  1389. **    --------------------------------------------------------------
  1390. **
  1391. ** On entry,
  1392. **    anchor        Parent anchor to link the this node to
  1393. **    address        Address of the directory
  1394. ** On exit,
  1395. **    returns        HT_LOADED if OK
  1396. **            <0 if error.
  1397. */
  1398. PRIVATE int read_directory
  1399. ARGS4 (
  1400.   HTParentAnchor *,        parent,
  1401.   CONST char *,            address,
  1402.   HTFormat,            format_out,
  1403.   HTStream *,            sink )
  1404. {
  1405.     int status;
  1406.     BOOLEAN WasInterrupted = FALSE;
  1407.     HTStructured* target = HTML_new(parent, format_out, sink);
  1408.     HTStructuredClass targetClass;
  1409.     char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
  1410.     EntryInfo *entry_info;
  1411.     BOOLEAN first=TRUE;
  1412.     char string_buffer[64];
  1413.  
  1414.     char c = 0;
  1415.  
  1416.     char *lastpath=0;  /* prefix for link, either "" (for root) or xxx  */
  1417.     char *entry;   /* pointer into lastpath to bit after last slash */
  1418.  
  1419.     targetClass = *(target->isa);
  1420.  
  1421.     _HTProgress ("Receiving FTP directory.");
  1422.     HTDirTitles(target, (HTAnchor*)parent);
  1423.   
  1424.     data_read_pointer = data_write_pointer = data_buffer;
  1425.  
  1426.     if (*filename == '\0')  /* Empty filename : use root */
  1427.         StrAllocCopy (lastpath, "/");
  1428.     else if(!strcmp(filename,"/"))  /* root path */
  1429.         StrAllocCopy (lastpath, "/foo/..");
  1430.     else 
  1431.     {
  1432.         char * p = strrchr(filename, '/');  /* find lastslash */
  1433.         StrAllocCopy(lastpath, p+1);    /* take slash off the beginning */
  1434.     }
  1435.     free (filename);
  1436.  
  1437.    
  1438.     {
  1439.         HTBTree * bt = HTBTree_new((HTComparer)compare_EntryInfo_structs);
  1440.         char c;
  1441.     HTChunk * chunk = HTChunkCreate(128);
  1442.     int BytesReceived = 0;
  1443.     int BytesReported = 0;
  1444.     char NumBytes[64];
  1445.     PUTS("\n");  /* prettier LJM */
  1446.     for (c=0; c!=(char)EOF;)   /* For each entry in the directory */
  1447.     {
  1448.         char * p = entry;
  1449.         HTChunkClear(chunk);
  1450.  
  1451.         if(HTCheckForInterrupt()) {
  1452.             WasInterrupted = TRUE;
  1453.         if(BytesReceived)
  1454.             goto unload_btree;  /* unload btree */
  1455.         else
  1456.           {
  1457.             ABORT_TARGET;
  1458.             HTBTreeAndObject_free(bt);
  1459.             return HT_INTERRUPTED;
  1460.           }
  1461.         }
  1462.  
  1463.         /*   read directory entry
  1464.          */
  1465.         for(;;) {                 /* Read in one line as filename */
  1466.         c = NEXT_DATA_CHAR;
  1467. AgainForMultiNet:
  1468.         if(interrupted_in_next_data_char) {
  1469.                 WasInterrupted = TRUE;
  1470.             if(BytesReceived)
  1471.                 goto unload_btree;  /* unload btree */
  1472.                     else
  1473.                       {
  1474.                         ABORT_TARGET;
  1475.                         HTBTreeAndObject_free(bt);
  1476.                         return HT_INTERRUPTED;
  1477.                       }
  1478.  
  1479.         } else if (c == CR || c == LF) {    /* Terminator? */ 
  1480.             if (chunk->size != 0) {  /* got some text */
  1481.                 /* Deal with MultiNet's wrapping of long lines */
  1482.                         if (server_type == VMS_SERVER) {
  1483.                         /* Deal with MultiNet's wrapping of long lines - F.M. */
  1484.                             if (data_read_pointer < data_write_pointer &&
  1485.                                 *(data_read_pointer+1) == ' ')
  1486.                                 data_read_pointer++;
  1487.                             else if (data_read_pointer >= data_write_pointer) {
  1488.                                 int status;
  1489.                                 status = NETREAD(data_soc, data_buffer,
  1490.                                                  DATA_BUFFER_SIZE);
  1491.                                 if (status == HT_INTERRUPTED) {
  1492.                                     interrupted_in_next_data_char = 1;
  1493.                                     goto AgainForMultiNet;
  1494.                                 }
  1495.                                 if (status <= 0) {
  1496.                                     c = (char)EOF;
  1497.                                     break;
  1498.                                 }
  1499.                                 data_write_pointer = data_buffer + status;
  1500.                                 data_read_pointer = data_buffer;
  1501.                                 if (*data_read_pointer == ' ')
  1502.                                     data_read_pointer++;
  1503.                                 else
  1504.                                     break;
  1505.                             }
  1506.                             else
  1507.                                 break;
  1508.                         }
  1509.             else
  1510.                     break;            /* finish getting one entry */
  1511.             }
  1512.         } else if (c == (char)EOF) {
  1513.             break;             /* End of file */
  1514.         } else {
  1515.             HTChunkPutc(chunk, c);
  1516.         }
  1517.             }
  1518.         HTChunkTerminate(chunk);
  1519.  
  1520.         BytesReceived += chunk->size;
  1521.         if (BytesReceived > BytesReported + 1024) {
  1522.             sprintf(NumBytes,"Transferred %d bytes",BytesReceived);
  1523.         HTProgress(NumBytes);
  1524.         BytesReported = BytesReceived;
  1525.         }
  1526.  
  1527.         if (c == (char) EOF && chunk->size == 1)
  1528.         /* 1 means empty: includes terminating 0 */
  1529.             break;
  1530.             if(TRACE)
  1531.              fprintf(stderr, "HTFTP: Line in %s is %s\n",
  1532.                  lastpath, chunk->data);
  1533.  
  1534.         entry_info = parse_dir_entry(chunk->data, &first);
  1535.         if(entry_info->display)
  1536.           {
  1537.          if(TRACE)
  1538.             fprintf(stderr,"Adding file to BTree: %s\n",
  1539.                 entry_info->filename);
  1540.              HTBTree_add(bt, (EntryInfo *)entry_info); 
  1541.           }
  1542.  
  1543.     }  /* next entry */
  1544.  
  1545. unload_btree:
  1546.  
  1547.         HTChunkFree(chunk);
  1548.  
  1549.     /* print out the handy help message if it exits :) */
  1550.     if(help_message_cache_non_empty()) {
  1551.         START(HTML_PRE);
  1552.         START(HTML_HR);
  1553.         PUTS(help_message_cache_contents());
  1554.         init_help_message_cache();  /* to free memory */
  1555.         START(HTML_HR);
  1556.     } else {
  1557.         START(HTML_PRE);
  1558.         PUTS("\n");
  1559.     }
  1560.  
  1561.     /* Put up header 
  1562.      */
  1563.     /* PUTS("    Date        Type             Size     Filename\n"); 
  1564.      */
  1565.        
  1566.     /* Run through tree printing out in order 
  1567.      */
  1568.     {
  1569.         HTBTElement * ele;
  1570.         int i;
  1571.         for (ele = HTBTree_next(bt, NULL);
  1572.          ele != NULL;
  1573.          ele = HTBTree_next(bt, ele))
  1574.         {
  1575.         entry_info = (EntryInfo *)HTBTree_object(ele);
  1576.  
  1577.         if(entry_info->date) 
  1578.                {
  1579.                      PUTS(entry_info->date);
  1580.                      PUTS("  ");
  1581.                }
  1582.         else
  1583.             PUTS("     * ");
  1584.  
  1585.         if(entry_info->type) 
  1586.           {
  1587.             for(i = 0; entry_info->type[i] != '\0' && i < 15; i++)
  1588.                 PUTC(entry_info->type[i]);
  1589.             for(; i < 17; i++)
  1590.                 PUTC(' ');
  1591.  
  1592.           }
  1593.  
  1594.         /* start the anchor */
  1595.         HTDirEntry(target, lastpath, entry_info->filename);  
  1596.         PUTS(entry_info->filename);
  1597.         END(HTML_A);
  1598.  
  1599.         if(entry_info->size) 
  1600.           {
  1601.                   if(entry_info->size < 1024)
  1602.                   sprintf(string_buffer,"  %d bytes",
  1603.                             entry_info->size);
  1604.               else
  1605.                   sprintf(string_buffer,"  %dKb",
  1606.                             entry_info->size/1024);
  1607.               PUTS(string_buffer);
  1608.           }
  1609.  
  1610.         PUTC('\n'); /* end of this entry */
  1611.  
  1612.         free_entryinfo_struct_contents(entry_info);
  1613.         }
  1614.     }
  1615.     FREE_TARGET;
  1616.     HTBTreeAndObject_free(bt);
  1617.     }
  1618.  
  1619.     if (lastpath) free(lastpath);
  1620.     if (WasInterrupted || HTCheckForInterrupt()) {
  1621.         response(NIL);
  1622.     _HTProgress("Data transfer interrupted.");
  1623.     return HT_LOADED;
  1624.     }
  1625.     response(NIL);
  1626.     return HT_LOADED;
  1627. /*    return response(NIL) == 2 ? HT_LOADED : -1; */
  1628. }
  1629.  
  1630. /*    Retrieve File from Server
  1631. **    -------------------------
  1632. **
  1633. ** On entry,
  1634. **    name        WWW address of a file: document, including hostname
  1635. ** On exit,
  1636. **    returns        Socket number for file if good.
  1637. **            <0 if bad.
  1638. */
  1639. PUBLIC int HTFTPLoad
  1640. ARGS4 (
  1641.   CONST char *,            name,
  1642.   HTParentAnchor *,        anchor,
  1643.   HTFormat,            format_out,
  1644.   HTStream *,            sink
  1645. )
  1646. {
  1647.     BOOL isDirectory = NO;
  1648.     int status;
  1649.     int retry;            /* How many times tried? */
  1650.     HTFormat format;
  1651.     char command[LINE_LENGTH+1];
  1652.     
  1653.  
  1654.     /* set use_list to NOT since we don't know what kind of server
  1655.      * this is yet.  And set the type to GENERIC
  1656.      */
  1657.     use_list = FALSE;
  1658.     server_type = GENERIC_SERVER;
  1659.  
  1660.     for (retry=0; retry<2; retry++) {    /* For timed out/broken connections */
  1661.     
  1662.     status = get_connection(name);
  1663.     if (status<0) return status;
  1664.  
  1665. #ifdef LISTEN
  1666.     status = get_listen_socket();
  1667.     if (status<0) {
  1668.         NETCLOSE (control->socket);
  1669.             control->socket = -1;
  1670.             close_master_socket ();
  1671.             /* HT_INTERRUPTED would fall through, if we could interrupt
  1672.                somehow in the middle of it, which we currently can't. */
  1673.         return status;
  1674.     }
  1675.     
  1676. #ifdef REPEAT_PORT
  1677. /*    Inform the server of the port number we will listen on
  1678. */
  1679.     {
  1680.         status = response(port_command);
  1681.         if (status == HT_INTERRUPTED) {
  1682.               if (TRACE)
  1683.                 fprintf (stderr,
  1684.              "FTP: Interrupted in response (port_command)\n");
  1685.               _HTProgress ("Connection interrupted.");
  1686.               NETCLOSE (control->socket);
  1687.               control->socket = -1;
  1688.               close_master_socket ();
  1689.               return HT_INTERRUPTED;
  1690.             }
  1691.         if (status !=2) {        /* Could have timed out */
  1692.         if (status<0) continue;        /* try again - net error*/
  1693.         return -status;            /* bad reply */
  1694.         }
  1695.         if (TRACE)
  1696.             fprintf(stderr, "FTP: Port defined.\n");
  1697.     }
  1698. #endif
  1699. #else    /* Use PASV */
  1700. /*    Tell the server to be passive
  1701. */
  1702.     {
  1703.         char *p;
  1704.         int reply, h0, h1, h2, h3, p0, p1;    /* Parts of reply */
  1705.         int status;
  1706.         data_soc = status;
  1707.  
  1708.         sprintf(command, "PASV%c%c", CR, LF);
  1709.         status = response(command);
  1710.         if (status !=2) {
  1711.         if (status<0) continue;        /* retry or Bad return */
  1712.         return -status;            /* bad reply */
  1713.         }
  1714.         for(p=response_text; *p && *p != ','; p++)
  1715.         ; /* null body */
  1716.  
  1717.         while (--p > response_text && '0' <= *p && *p <= '9')
  1718.         ; /* null body */
  1719.     
  1720.            status = sscanf(p+1, "%d,%d,%d,%d,%d,%d",
  1721.                    &h0, &h1, &h2, &h3, &p0, &p1);
  1722.            if (status<4) {
  1723.                fprintf(stderr, "FTP: PASV reply has no inet address!\n");
  1724.                return -99;
  1725.            }
  1726.            passive_port = (p0<<8) + p1;
  1727.        if(TRACE)
  1728.                fprintf(stderr, "FTP: Server is listening on port %d\n",
  1729.                           passive_port);
  1730.  
  1731.  
  1732. /*    Open connection for data:
  1733. */
  1734.         sprintf(command,
  1735.             "ftp://%d.%d.%d.%d:%d/",h0,h1,h2,h3,passive_port);
  1736.             status = HTDoConnect(name, "FTP", passive_port, &data_soc);
  1737.  
  1738.         if (status<0){
  1739.         (void) HTInetStatus("connect for data");
  1740.         NETCLOSE(data_soc);
  1741.         return status;            /* Bad return */
  1742.         }
  1743.         
  1744.         if (TRACE)
  1745.             fprintf(stderr, "FTP data connected, socket %d\n", data_soc);
  1746.     }
  1747. #endif /* use PASV */
  1748.     status = 0;
  1749.         break;    /* No more retries */
  1750.  
  1751.     } /* for retries */
  1752.     if (status<0) return status;    /* Failed with this code */
  1753.     
  1754. /*    Ask for the file:
  1755. */    
  1756.     {
  1757.         char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
  1758.     char *fname = filename;    /** Save for subsequent free() **/
  1759.     BOOL binary;
  1760.     HTAtom * encoding;
  1761.  
  1762.     if (!*filename) StrAllocCopy(filename, "/");
  1763.     HTUnEscape(filename);
  1764.     if (TRACE)
  1765.         fprintf(stderr, "FTP: UnEscaped %d\n", filename);
  1766.     format = HTFileFormat(filename, &encoding);
  1767.     binary = (encoding != HTAtom_for("8bit")
  1768.           && encoding != HTAtom_for("7bit"));
  1769.         if (binary != control->binary) {
  1770.         char * mode = binary ? "I" : "A";
  1771.         sprintf(command, "TYPE %s%c%c", mode, CR, LF);
  1772.         status = response(command);
  1773.         if (status != 2)
  1774.             return ((status < 0) ? status : -status);
  1775.         control->binary = binary;
  1776.     }
  1777.     if (server_type == VMS_SERVER) {
  1778.         char *cp, *cp1, *cp2;
  1779.         /** Accept only Unix-style filename **/
  1780.         if (strchr(filename, ':') != NULL ||
  1781.             strchr(filename, '[') != NULL) {
  1782.         free(fname);
  1783.         return -1;
  1784.         }
  1785.         /** Trim trailing slash if filename is not the top directory **/
  1786.         if (strlen(filename) > 1 && filename[strlen(filename)-1] == '/')
  1787.             filename[strlen(filename)-1] = '\0';
  1788.  
  1789. #ifdef MAINTAIN_CONNECTION /* Don't need this if always new connection - F.M. */
  1790.         /** Get the current default VMS device:[directory] **/ 
  1791.         sprintf(command, "PWD%c%c", CR, LF);
  1792.         status = response (command);
  1793.         if (status != 2) {
  1794.              free(fname);
  1795.          return ((status < 0) ? status : -status);
  1796.         }
  1797.         /** Go to the VMS account's top directory **/
  1798.         if ((cp=strchr(response_text, '[')) != NULL &&
  1799.             (cp1=strrchr(response_text, ']')) != NULL) {
  1800.         sprintf(command, "CWD %s", cp);
  1801.         if ((cp2=strchr(cp, '.')) != NULL && cp2 < cp1)
  1802.             sprintf(command+(cp2-cp)+4, "]%c%c", CR, LF);
  1803.         else
  1804.             sprintf(command+(cp1-cp)+4, "]%c%c", CR, LF);
  1805.         status = response (command);
  1806.         if (status != 2) {
  1807.             free(fname);
  1808.             return ((status < 0) ? status : -status);
  1809.         }
  1810.         }
  1811. #endif /* MAINTAIN_CONNECTION */
  1812.  
  1813.         /** If we want the VMS account's top directory, list it now **/
  1814.         if (strlen(filename) == 1 && *filename == '/') {
  1815.         isDirectory = YES;
  1816.         sprintf(command, "LIST%c%c", CR, LF);
  1817.         status = response (command);
  1818.         free(fname);
  1819.         if (status != 1)
  1820.             /* Action not started */
  1821.             return ((status < 0) ? status : -status);
  1822.  
  1823.         goto listen;  /* big goto */
  1824.         }
  1825.         /** Otherwise, go to appropriate directory and doctor filename **/
  1826.         if ((cp=strchr(filename, '/')) != NULL &&
  1827.             (cp1=strrchr(cp, '/')) != NULL && cp != cp1) {
  1828.         sprintf(command, "CWD [.%s", cp+1);
  1829.         sprintf(command+(cp1-cp)+5, "]%c%c", CR, LF);
  1830.         while ((cp2=strrchr(command, '/')) != NULL)
  1831.             *cp2 = '.';
  1832.         status = response(command);
  1833.         if (status != 2) {
  1834.             free(fname);
  1835.             return ((status < 0) ? status : -status);
  1836.         }
  1837.         filename = cp1+1;
  1838.         }
  1839.         else
  1840.             filename += 1;
  1841.  
  1842.     }
  1843.     sprintf(command, "RETR %s%c%c", filename, CR, LF);
  1844.     status = response(command);
  1845.     if (status != 1) {  /* Failed : try to CWD to it */
  1846.  
  1847.        /* Clear any login messages if this isn't the login directory */
  1848.       if(strcmp(filename, "/"))
  1849.           init_help_message_cache();
  1850.  
  1851.       sprintf(command, "CWD %s%c%c", filename, CR, LF);
  1852.       status = response(command);
  1853.  
  1854.       if (status == 2) {  /* Successed : let's NAME LIST it */
  1855.         isDirectory = YES;
  1856.         if(use_list)
  1857.             sprintf(command, "LIST%c%c", CR, LF);
  1858.         else
  1859.             sprintf(command, "NLST%c%c", CR, LF);
  1860.         status = response (command);
  1861.       }
  1862.     }
  1863.     free(fname);
  1864.     if (status != 1)
  1865.       {
  1866.         if(status < 0)
  1867.           return status;
  1868.         else
  1869.           return -status;
  1870.       }
  1871.  
  1872.     }
  1873.  
  1874. listen:
  1875. #ifdef LISTEN
  1876. /*    Wait for the connection
  1877. */
  1878.     {
  1879.     struct sockaddr_in soc_address;
  1880.         int    soc_addrlen=sizeof(soc_address);
  1881. #ifdef SOCKS
  1882.     status = Raccept(master_socket,
  1883. #else
  1884.     status = accept(master_socket,
  1885. #endif /* SOCKS */
  1886.             (struct sockaddr *)&soc_address,
  1887.             &soc_addrlen);
  1888.     if (status<0)
  1889.         return HTInetStatus("accept");
  1890.     CTRACE(tfp, "TCP: Accepted new socket %d\n", status);
  1891.     data_soc = status;
  1892.     }
  1893. #else
  1894. /* @@ */
  1895. #endif
  1896.     if (isDirectory) {
  1897.         status = read_directory (anchor, name, format_out, sink);
  1898.         NETCLOSE(data_soc);
  1899.     NETCLOSE(control->socket);
  1900.         return status;
  1901.       /* returns HT_LOADED or error */
  1902.     } else {
  1903.         int rv;
  1904.  
  1905.     _HTProgress ("Receiving FTP file.");
  1906.     rv = HTParseSocket(format, format_out, anchor, data_soc, sink);
  1907.  
  1908.     if (rv == HT_INTERRUPTED)
  1909.          _HTProgress("Data transfer interrupted.");
  1910.  
  1911.     HTInitInput(control->socket);
  1912.     /* Reset buffering to control connection DD 921208 */
  1913.     
  1914.     status = NETCLOSE(data_soc);
  1915.     if (TRACE)
  1916.         fprintf(stderr, "FTP: Closing data socket %d\n", data_soc);
  1917.     if (status<0 && rv != HT_INTERRUPTED && rv != -1)
  1918.         (void) HTInetStatus("close");    /* Comment only */
  1919.     data_soc = -1;    /* invalidate it */
  1920.     
  1921.     status = response(NIL);        /* Pick up final reply */
  1922.     if (status!=2 && rv != HT_INTERRUPTED && rv != -1)
  1923.         return HTLoadError(sink, 500, response_text);
  1924.  
  1925.     NETCLOSE(control->socket);
  1926.     return HT_LOADED;
  1927.     }       
  1928. } /* open_file_read */
  1929.